﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace FerrSimplicity
{
    public enum TextAlign
    {
        Left   = 1,
        Right  = 2,
        Top    = 4,
        Bottom = 8,
        Center = 16
    }
    class SystemTextData
    {
        public FontStyle style;
        public Vector3   pos;
        public Vector3   vel;
        public string    text;
        public float     time;
        public float     scale;
        public float     initialLife;
    }
    public class SystemText
    {
        #region Singleton Code
        private static SystemText mInstance = null;
        public  static SystemText Instance { get { if (mInstance == null) mInstance = new SystemText(); return mInstance; } }
        private SystemText()
        {
            mText = new List<SystemTextData>();
        }
        #endregion

        #region Fields
        List<SystemTextData> mText;
        char[]               mSplitChars = { '<', '>' };
        #endregion

        #region Adding Text
        public void AddTextComplex(string aText, Vector2 aPos, float aLife)
        {
            string[]  lines        = aText.Split('\n');
            FontStyle currentStyle = ManagerFontStyle.Instance.GetDefault();
            Color     color        = Color.White;
            Vector2   pos          = aPos;

            // separate lines so we can calculate positions
            for (int line = 0; line < lines.Length; line++)
            {
                float lineSize = currentStyle.font.MeasureString("l").Y;

                string[] segments = lines[line].Split(mSplitChars);

                for (int i = 0; i < segments.Length; i++)
                {
                    // odd value, we're looking at a style tag
                    if (i % 2 == 1)
                    {
                        string[] tags = segments[i].Trim().Split(' ');
                        if (tags.Length > 0)
                            currentStyle = ManagerFontStyle.Instance.GetStyle(tags[0]);
                        if (tags.Length > 1)
                        {
                            string[] sc = tags[1].Split(new char[] { ',' });
                            if (sc.Length == 3)
                                color = new Color(float.Parse(sc[0]), float.Parse(sc[1]), float.Parse(sc[2]));
                            else if (sc.Length == 4)
                                color = new Color(float.Parse(sc[0]), float.Parse(sc[1]), float.Parse(sc[2]), float.Parse(sc[3]));
                        }
                    }
                    else if (segments[i].Length > 0)
                    {
                        AddText(segments[i], new Vector3(pos, float.MaxValue), Vector3.Zero, currentStyle, aLife, 1);
                        // advance to the next piece of text
                        pos.X += currentStyle.font.MeasureString(segments[i]).X;

                        // check the height of this piece of text
                        float tSize = currentStyle.font.MeasureString("l").Y;
                        if (tSize > lineSize)
                            lineSize = tSize;
                    }
                }
                // advance down to the next line
                pos.X = aPos.X;
                pos.Y += lineSize;// +currentStyle.font.LineSpacing;
            }
        }

        public int  CountLines    (string aText, float aBoundsWidth, FontStyle aDefaultStyle)
        {
            string[] segments = aText.Split(mSplitChars);

            // add all of the words
            List<string>    words   = new List<string>   ();
            List<int>       segSize = new List<int>      ();
            List<FontStyle> style   = new List<FontStyle>();

            if (aDefaultStyle != null)
                style.Add(aDefaultStyle);
            else
                style.Add(ManagerFontStyle.Instance.GetDefault());
            for (int i = 0; i < segments.Length; i++)
            {
                if (i % 2 == 0)
                {
                    string[] lines = segments[i].Split(' ');
                    words.AddRange(lines);
                    segSize.Add(lines.Length);
                }
                else
                {
                    style.Add(ManagerFontStyle.Instance.GetStyle(segments[i]));
                }
            }


            // now go ahead and parse/add those words
            int     currSeg    = 0;
            int     segCounter = 0;
            Vector2 pos        = new Vector2(0, 0);
            int     lineCount  = 0;

            for (int i = 0; i < words.Count; i++)
            {
                // check to see if we've advanced to the next text segment
                if (segCounter >= segSize[currSeg])
                {
                    currSeg++;
                    segCounter = 0;
                }

                // check to see if there are any newlines in this word
                bool newLine = words[i].Contains('\n');
                if (newLine)
                    words[i] = words[i].Replace("\n", "");

                // get the size of the piece we're currently working with
                Vector2 size = style[currSeg].font.MeasureString(words[i] + ' ');

                // check if we've gone too far right
                if (pos.X + size.X > aBoundsWidth || newLine)
                {
                    lineCount++;
                    pos.X = 0;
                }

                pos.X += size.X;
                segCounter++;
            }

            return lineCount;
        }
        public void AddTextComplex(string aText, Rectangle aBounds, int aStartLine, TextAlign aAlignBitflag)
        {
            string[] segments = aText.Split(mSplitChars);

            // add all of the words
            List<string>    words   = new List<string>   ();
            List<int>       segSize = new List<int>      ();
            List<FontStyle> style   = new List<FontStyle>();

            style.Add(ManagerFontStyle.Instance.GetDefault());
            for (int i = 0; i < segments.Length; i++)
            {
                if (i % 2 == 0)
                {
                    string[] lines = segments[i].Split(' ');
                    words.AddRange(lines);
                    segSize.Add(lines.Length);
                }
                else
                {
                    style.Add(ManagerFontStyle.Instance.GetStyle(segments[i]));
                }
            }


            // now go ahead and parse/add those words
            Vector2 pos = new Vector2(aBounds.X, aBounds.Y);
            int   currSeg    = 0;
            int   segCounter = 0;
            int   lineCount  = 0;
            float maxHeight  = 0;
            float maxSpacing = 0;

            for (int i = 0; i < words.Count; i++)
            {
                // check to see if we've advanced to the next text segment
                if (segCounter >= segSize[currSeg])
                {
                    currSeg++;
                    segCounter = 0;

                    if (style[currSeg].font.LineSpacing > maxSpacing)
                        maxSpacing = style[currSeg].font.LineSpacing;
                }

                // check to see if there are any newlines in this word
                bool newLine = words[i].Contains('\n');
                if (newLine)
                    words[i] = words[i].Replace("\n", "");

                // get the size of the piece we're currently working with
                Vector2 size = style[currSeg].font.MeasureString(words[i] + ' ');

                // get the maximum height on this line
                if (size.Y > style[currSeg].font.LineSpacing)
                    maxHeight = size.Y;

                // check if we've gone too far right
                if (pos.X + size.X > aBounds.Right || newLine)
                {
                    if (lineCount >= aStartLine)
                        pos.Y += maxHeight;
                    lineCount++;
                    pos.X = aBounds.X;
                    maxHeight = 0;
                    maxSpacing = style[currSeg].font.LineSpacing;
                }

                // if it goes off the bottom, we need to stop
                if (pos.Y + size.Y > aBounds.Bottom)
                    return;

                if (lineCount >= aStartLine)
                    AddText(words[i] + ' ', pos, style[currSeg]);
                pos.X += size.X;
                segCounter++;
            }
        }

        public void AddText(string aText, Vector2 aPos)
        {
            AddText(aText, new Vector3(aPos, float.MaxValue), Vector3.Zero, ManagerFontStyle.Instance.GetDefault(), 0, 1);
        }
        public void AddText(string aText, Vector2 aPos, FontStyle aStyle)
        {
            AddText(aText, new Vector3(aPos, float.MaxValue), Vector3.Zero, aStyle, 0, 1);
        }
        public void AddText(string aText, Vector2 aPos, float aLife)
        {
            AddText(aText, new Vector3(aPos, float.MaxValue), Vector3.Zero, ManagerFontStyle.Instance.GetDefault(), aLife, 1);
        }
        public void AddText(string aText, Vector2 aPos, float aLife, float aScale)
        {
            AddText(aText, new Vector3(aPos, float.MaxValue), Vector3.Zero, ManagerFontStyle.Instance.GetDefault(), aLife, aScale);
        }
        public void AddText(string aText, Vector3 aPos)
        {
            AddText(aText, aPos, Vector3.Zero, ManagerFontStyle.Instance.GetDefault(), 0, 1);
        }
        public void AddText(string aText, Vector3 aPos, Vector3 aVel, FontStyle aStyle, float aLife, float aScale)
        {
            SystemTextData tmt = new SystemTextData();
            tmt.initialLife = aLife;
            tmt.time        = aLife;
            tmt.pos         = aPos;
            tmt.text        = aText;
            tmt.vel         = aVel;
            tmt.style       = aStyle;
            tmt.scale       = aScale;

            mText.Add(tmt);
        }
        #endregion

        #region Draw
        public void Draw(GameTime aTime)
        {
            FerrGlobals.SpriteBatch.Begin();
            for (int i = 0; i < mText.Count; i++)
            {
                Vector2 pos = Vector2.Zero;
                if (mText[i].pos.Z == float.MaxValue)
                    pos = new Vector2(mText[i].pos.X, mText[i].pos.Y);
                else
                {
                    throw new NotImplementedException("3D based text locations not implemented just yet! sorry~");
                }
                Vector2 origin = Vector2.Zero;
                //mText[i].style.font.MeasureString(mText[i].text) / 2;
                FerrGlobals.SpriteBatch.DrawString(mText[i].style.font, mText[i].text, new Vector2(pos.X, pos.Y), mText[i].style.tint, 0, origin, mText[i].scale, SpriteEffects.None, 0.5f);

                // remove the text if it's too old
                mText[i].time -= (float)aTime.ElapsedGameTime.TotalSeconds;
                if (mText[i].time <= 0)
                {
                    mText.RemoveAt(i);
                    i--;
                }
                else
                {
                    // this is a mild hack, as this could potentially be called multiple times a frame
                    mText[i].pos += mText[i].vel * (float)aTime.ElapsedGameTime.TotalSeconds;
                }
            }
            FerrGlobals.SpriteBatch.End();
        }
        #endregion
    }
}
